/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.util;

import static org.junit.Assert.*;
import java.util.*;
import org.junit.*;
import ch.trackedbean.copier.*;
import ch.trackedbean.data.*;
import ch.trackedbean.util.ListMergeHelper.*;

/**
 * Test for {@link ListMergeHelper}.
 * 
 * @author M. Hautle
 */
public class ListMerge {
	// CONTINUE restliche methoden abdecken
	public List<DomA> init(int number) {
		final List<DomA> src = new ArrayList<DomA>();
		for (int i = 0; i < number; i++)
			src.add(DomFactory.fillDomA(new DomA(), i, "a", "b", 1));
		return src;
	}

	private List<TOA> convert(List<DomA> doms) {
		final List<TOA> list = new ArrayList<TOA>();
		for (DomA dom : doms)
			list.add(BeanMapperManager.copySrc2Dst(dom, new TOA()));
		return list;
	}

	/**
	 * Test for {@link ListMergeHelper#getElement(Object)} and {@link ListMergeHelper#getElement(Object...)}.
	 */
	@Test
	public void testGetElement() {
		final List<DomA> src = init(5);
		final List<TOA> res = convert(src);
		Collections.shuffle(src);
		Collections.shuffle(res);
		final CrossElementComparator<DomA> cmp = new CrossElementComparator<DomA>(DomA.class, DomA.ATTR_NUMBER_A);
		final ListMergeHelper<DomA> merge = new ListMergeHelper<DomA>(cmp).addElements(src);
		cmp.addAccessors(TOA.class, TOA.ATTR_NUMBER_A);
		for (TOA t : res)
			check(t, merge.getElement(t));
		for (TOA t : res)
			check(t, merge.getElement(new Object[] { new Integer(t.getNumberA()) }));
	}

	/**
	 * Test for {@link ListMergeHelper#removeAll(Collection)} and {@link ListMergeHelper#removeAllMerged(List)}.
	 */
	@Test
	public void testRemoveAll() {
		final List<DomA> src = init(5);
		final List<DomA> res = new ArrayList<DomA>(src);
		final List<TOA> rem = new ArrayList<TOA>(convert(src));
		final TOA surv = rem.remove(0);
		Collections.shuffle(src);
		Collections.shuffle(res);
		final CrossElementComparator<DomA> cmp = new CrossElementComparator<DomA>(DomA.class, DomA.ATTR_NUMBER_A);
		final ListMergeHelper<DomA> merge = new ListMergeHelper<DomA>(cmp).addElements(src);
		cmp.addAccessors(TOA.class, TOA.ATTR_NUMBER_A);
		merge.removeAll(rem);
		assertTrue("Not expected size", merge.getSize() == 1);
		check(surv, merge.getElement(surv));
		assertTrue("Element not removed", merge.removeAllMerged(res).size() == 1);
		assertTrue("Not expected size", rem.size() == res.size());
		Collections.sort(res, cmp);
		Collections.sort(rem, (Comparator) cmp);
		for (int i = 0, cnt = res.size(); i < cnt; i++)
			check(rem.get(i), res.get(i));
	}

	/**
	 * Test for {@link ListMergeHelper#retainAll(Collection)} and {@link ListMergeHelper#retainAllMerged(List)}.
	 */
	@Test
	public void testRetainAll() {
		final List<DomA> src = init(10);
		final List<DomA> res = new ArrayList<DomA>(src);
		final List<TOA> ret = new ArrayList<TOA>(convert(src));
		Collections.shuffle(src);
		Collections.shuffle(res);
		Collections.shuffle(ret);
		final CrossElementComparator<DomA> cmp = new CrossElementComparator<DomA>(DomA.class, DomA.ATTR_NUMBER_A);
		final ListMergeHelper<DomA> merge = new ListMergeHelper<DomA>(cmp).addElements(src);
		cmp.addAccessors(TOA.class, TOA.ATTR_NUMBER_A);
		for (int i = 0; i < 5; i++)
			ret.remove(0);
		// first we remove all 'deleted' TO's
		merge.retainAll(ret);
		assertTrue("Not expected size", merge.getSize() == ret.size());
		for (TOA t : ret)
			check(t, merge.getElement(t));
		// now we adapt the res list so that it's equals to ret
		merge.retainAllMerged(res);
		assertTrue("Not expected size", res.size() == ret.size());
		Collections.sort(res, cmp);
		Collections.sort(ret, (Comparator) cmp);
		for (int i = 0, cnt = res.size(); i < cnt; i++)
			check(ret.get(i), res.get(i));
	}

	/**
	 * Test for {@link ListMergeHelper#retainAll(Collection)} and {@link ListMergeHelper#retainAllMerged(List)} by using empty collections.
	 */
	@Test
	public void testRetainAllEmpty() {
		final List<DomA> src = init(10);
		Collections.shuffle(src);
		final CrossElementComparator<DomA> cmp = new CrossElementComparator<DomA>(DomA.class, DomA.ATTR_NUMBER_A);
		final ListMergeHelper<DomA> merge = new ListMergeHelper<DomA>(cmp).addElements(src);
		cmp.addAccessors(TOA.class, TOA.ATTR_NUMBER_A);
		merge.retainAll(Collections.emptyList());
		assertTrue("Not expected size", merge.getSize() == 0);
		merge.retainAllMerged(src);
		assertTrue("Not expected size", src.size() == 0);
	}

	@Test
	public void testMerge() {
		final List<DomA> src = init(10);
		final List<TOA> ret = new ArrayList<TOA>(convert(src));
		// drop first dom -> this will be added as to
		final DomA newOne = src.remove(0);
		// drop second to -> this dom has to be removed
		final TOA removed = ret.remove(1);
		Collections.shuffle(src);
		Collections.shuffle(ret);
		final CrossElementComparator<DomA> cmp = new CrossElementComparator<DomA>(DomA.class, DomA.ATTR_NUMBER_A);
		final ListMergeHelper<DomA> merge = new ListMergeHelper<DomA>(cmp).addElements(src);
		cmp.addAccessors(TOA.class, TOA.ATTR_NUMBER_A);
		final MergeHelper merger = new MergeHelper(newOne, removed, src, ret);
		merge.merge(ret, merger);
		merger.finalCheck();
	}

	private static void check(TOA to, DomA dom) {
		assertEquals(dom.getGetterA(), to.getGetterA());
		assertEquals(dom.getNumberA(), to.getNumberA());
		assertEquals(dom.getTextA(), to.getTextA());
	}

	/**
	 * Cleans up
	 * 
	 * @throws Exception
	 */
	@After
	public void tearDown() throws Exception {
		BeanUtils.clearCache();
	}

	private static class MergeHelper implements IMergeHandler<DomA, TOA> {

		private boolean add = false;

		private boolean remove = false;

		private final DomA newOne;

		private final TOA removed;

		private List<DomA> doms;

		private List<TOA> tos;

		/**
		 * @param newOne
		 * @param removed
		 * @param doms
		 * @param tos
		 */
		public MergeHelper(DomA newOne, TOA removed, List<DomA> doms, List<TOA> tos) {
			super();
			this.newOne = newOne;
			this.removed = removed;
			this.doms = new ArrayList<DomA>(doms);
			this.tos = new ArrayList<TOA>(tos);
		}

		@Override
		public void added(TOA el) {
			assertFalse(add);
			check(el, newOne);
			add = true;
		}

		@Override
		public void merged(TOA src, DomA dst) {
			check(src, dst);
			doms.remove(dst);
			tos.remove(src);
		}

		@Override
		public void removed(DomA el) {
			assertFalse(remove);
			check(removed, el);
			remove = true;
		}

		public void finalCheck() {
			assertTrue("remove", remove);
			assertTrue("add", add);
			assertTrue("merge", doms.size() == 1);
			assertTrue("merge", tos.size() == 1);
			check(removed, doms.get(0));
			check(tos.get(0), newOne);
		}
	}
}
